package rayTracer;

import static rayTracer.ApplicationInterface.*;
import helper.SceneFileParser;
import helper.Stopwatch;
import static helper.Util.*;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;

import javax.imageio.ImageIO;
import javax.swing.JFrame;


public class RayTracerApplication {
	private String outputFile;
	private String inputFile;
	private String outputFormat;
	
	private boolean timeMeasuringMode;
	private boolean showImageMode;
	private boolean showProgressMode;
	private RayTracer raytracer;
	
	public RayTracerApplication() {
		//Set default values
		inputFile = DEFAULT_SCENE;		
		outputFile = OUT_FILE;
		outputFormat = DEFAULT_EXTENSION;
		
		timeMeasuringMode = false;	
		showImageMode = false;
		showProgressMode = false;
		
		raytracer = new RayTracer();		
	}
	
	public String getOutputFileName(){
		return outputFile;
	}
	
	public String getInputFileName(){
		return inputFile;
	}
	
	public String getOutputFormat(){
		return outputFormat;
	}
	public RayTracer getTracer(){		
		return raytracer;
	}
	
	//For debugging
	public String toString(){		
		StringBuffer str = new StringBuffer();		
		str.append("Application Parameters :  \n");
		str.append("input = "+inputFile+"\toutput = "+outputFile+"\t\toutputFormat = "+outputFormat+"\n");		
		str.append("time = " + timeMeasuringMode);
		str.append("\tshow = " + showImageMode);
		str.append("\tprogress = " + showProgressMode+"\n");
		return str.toString();
	}
	
	public boolean parseCommandLine( String[] args){
		String stringValue = null;	
		
		for(int i=0 ; i<args.length; i++){
			if( args[i].charAt(0) == '-' ){				 
				int index = getParamIndex( args[i].substring(1) );
				String option = args[i];
				
				if( index == -1 ){
					System.out.println("El switch "+option+" no es valido.");
					return false;
				}				
				if( isUnarySwitch(index) )
					stringValue = null;
				else{
					if( args[i+1].charAt(0) == '-'){
						System.out.println("El switch " + option + " esperaba un parametro.");
						return false;
					}else{
						stringValue = args[i+1].substring(0);
						i++;
					}
				}
				
				switch( index ){
					case 0: inputFile = new String(stringValue);
							int pos =  stringValue.lastIndexOf('.');
							if( pos == 0 || pos == -1 || pos == stringValue.length()-1 ){								
								System.out.println("Nombre de archivo invalido.");
								return false;
							}
							int barIndex = -1;
							if( (barIndex = stringValue.lastIndexOf("/")) == -1 )
								barIndex = 0;
								
							outputFile = new String(stringValue.substring(barIndex+1,pos));
							break;
					case 1: if( processNameAndExtension(stringValue) == false )
								return false;
							break;					
					case 2: timeMeasuringMode = true; break;
					case 3: showProgressMode = true; break;
					case 4: showImageMode = true; break;
				}				
			}else{
				System.out.println("Error en la sintaxis del comando");
				return false;
			}
		}
		return true;
	}
	
	
	
	private boolean processNameAndExtension(String stringValue){
		int pos =  stringValue.lastIndexOf('.');
		if( pos == 0 || pos == stringValue.length()-1 ){								
			System.out.println("Nombre de archivo invalido.");
			return false;
		}	
		
		String ext = null;
		String name = null;
		if( pos == -1 ){
			ext = DEFAULT_EXTENSION;
			name = stringValue;
		}else{
			ext = stringValue.substring(pos+1, stringValue.length());
			name = stringValue.substring(0,pos);
		}					 	
	 	
	 	if( !ext.isEmpty() ){
	 		if( ext.compareToIgnoreCase("bmp") != 0 && ext.compareToIgnoreCase("png") != 0 ){
	 			System.out.println("Solo los formatos bmp y png son soportados");
	 			return false;
	 		}
	 		else{
	 			if( !isValidString(ext) ){
	 				System.out.println("Extension del archivo de salida invalido");
		 			return false;
	 			}
	 			outputFormat = new String(ext);
	 		}
	 	}
	 		
	 	if( !name.isEmpty()){
	 		if( !isValidString(name) ){
	 			System.out.println("Nombre de archivo de salida invalido");
	 			return false;
	 		}						 			
	 		outputFile = new String(name);
	 	}
	 	return true;
	}
	
	
	
	private boolean isValidString(String str){
		boolean isValid = true;
		for( int j=0 ; isValid && j<str.length() ; j++ ){
 			char c = str.charAt(j);
 			if( !Character.isLetterOrDigit(c) && c != '_' && c != '.' )
 				isValid = false;
 		}
		return isValid;
	}
	private int getParamIndex(String string) {
		
		for(int i=0 ; i< validSwitches.length ; i++){
			if( string.compareToIgnoreCase( validSwitches[i] ) == 0 ) 
					return i;
		}
		return -1;
	}
	
	private boolean isUnarySwitch(int index) {		
		return index == 2 || index ==3 || index == 4;
	}
	
	//Aplicacion principal
	public static void main(String[] args) {
		//Timer to measure how long it takes to prepare the caster
		Stopwatch preparationTimer = new Stopwatch().start();
		
		RayTracerApplication app = new RayTracerApplication();
		if( !app.parseCommandLine(args)){
			showUsage();
			return;
		}		
		System.out.println(app);
		
		Pixel[] image;
		//Puede tirar exception por parseSceneFile() o por caster.render()
		try {
			RayTracer tracer = app.getTracer();
			SceneFileParser scParser = new SceneFileParser(tracer);
			if( scParser.parseSceneFile(app.getInputFileName()) != 0 )
				return;
			System.out.println("Construyendo Octree ... ");
			//Aqui se construye el octree
			app.showProgressBar();
			tracer.scene.prepareScene();			
			preparationTimer.stop();
			
			//How long the render takes
			Stopwatch renderTimer = new Stopwatch().start();			
			image = tracer.render();			
			renderTimer.stop();
			
			Stopwatch ioTimer = new Stopwatch().start();			
			if( !saveImage(image, tracer.width, tracer.height, 
							app.getOutputFileName(), app.getOutputFormat()) )
				System.out.println("Caster Application : Errors while trying to save image");
			ioTimer.stop();
			
			app.showTimerResults(preparationTimer, renderTimer, ioTimer);			
			System.out.println("Done !!\n");			
			app.showRenderedImage(app.getOutputFileName(), app.getOutputFormat());
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("Caster Application : "+ e.getMessage());			
		}		
	}
	
	public void showProgressBar() {		 
		if( showProgressMode ){
			(new Thread(new ProgressBar(raytracer))).start();			
		}
	}
	
	public void showRenderedImage(String filename, String format){
		if( showImageMode ){
			StringBuffer buf = new StringBuffer();
			buf.append("img/"+filename+"."+format);
			JFrame frame = new JFrame("Ray Tracer rendered image : " + buf.toString());
		    Panel panel = new ShowImage(buf.toString());
		    frame.getContentPane().add(panel); 
		    frame.setSize(raytracer.width, raytracer.height);
		    frame.setVisible(true);
		}
	}
	
	public void showTimerResults(Stopwatch preparationTimer, Stopwatch renderTimer, Stopwatch ioTimer) {
		if( timeMeasuringMode ){
			System.out.println("Time Results :");
			System.out.println("Preparation Time = " + preparationTimer.getElapsedTime() + " ms");
			System.out.println("Render Time = " + renderTimer.getElapsedTime() + " ms");
			System.out.println("File IO Time = " + ioTimer.getElapsedTime() + " ms");
		}		
	}
	
	private static void showUsage(){
		System.out.println("USAGE:");
		System.out.println("======\n");
		System.out.println("raytracer -i < file.sc >  [-o outputFileName.extension ] ");
		System.out.println("[-time]  [-progress]  [-show]");
		System.out.println("\nExample:  raytracer -show -i hand.sc -o render.png -time -progress");
	}

	class ProgressBar implements Runnable{
		RayTracer rc;
		
		public ProgressBar(RayTracer rayCaster){
			rc = rayCaster;
		}
		
		public void run(){
			while( rc.percentage < 100 ){
				try{
					Thread.sleep(200);
				} catch (InterruptedException e) {} 
		        System.out.print("\r"+displayBar((int)(rc.percentage))+" "+(int)rc.percentage+"%"); 
		    } 
			System.out.println();
		}
		
		private String displayBar(int num) 
		{ 
		    StringBuffer sb = new StringBuffer(); 

		    int x = num / 2; 
		    sb.append("["); 
		    for (int k = 0; k < 50; k++) 
		        sb.append( ((x <= k) ? " " : "=")); 
		    sb.append("]"); 

		    return sb.toString(); 
		} 
	}
	
	public class ShowImage extends Panel {    
		private static final long serialVersionUID = 8160172478300881585L;
		BufferedImage  image;
		
	    public ShowImage(String filename) {
	    	try {	    		
	    		File input = new File(filename);
	    		image = ImageIO.read(input);
	    	} catch (IOException ie) {
	    	System.out.println("Error:"+ie.getMessage());
	    }
	  }

	  public void paint(Graphics g) {
	    g.drawImage( image, 0, 0, null);
	  }	
	} 

}
